/*
 * Copyright (c) 2007-2021 西安交通信息投资营运有限公司 版权所有
 */

package com.xci.xajtfb.bus.service;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.github.lvyanyang.annotation.OperateLog;
import com.github.lvyanyang.core.*;
import com.github.lvyanyang.exceptions.ValidException;
import com.github.lvyanyang.model.CaptchaDataModel;
import com.xci.xajtfb._core.SmsProperties;
import com.xci.xajtfb._core.XajtfbService;
import com.xci.xajtfb.bus._core.BusBaseService;
import com.xci.xajtfb.bus.entity.WxMember;
import com.xci.xajtfb.bus.dao.WxMemberDao;
import com.xci.xajtfb.bus.filter.WxMemberFilter;
import com.xci.xajtfb.bus.model.BuildPhoneCaptchaBody;
import com.xci.xajtfb.bus.model.SendPhoneCaptchaBody;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;

/**
 * 投诉用户表服务
 * @author 吕艳阳 lvyanyang@xci96716.com
 * @since 2021-03-03 17:35:58
 */
@Service
@Slf4j
public class WxMemberService extends BusBaseService {
    public static final String PHONE_CAPTCHA_CACHE_KEY = "phone_captcha_cache_key";
    /** 投诉用户表数据层对象 */
    @Resource private WxMemberDao wxmemberDao;
    /** 手机验证码缓存 */
    @Resource private Cache phoneCaptchaCache;
    @Resource private Cache phoneDayTimesLimitCache;
    @Resource private XajtfbService xajtfbService;
    @Resource private RestTemplate restTemplate;
    @Resource private SmsProperties smsProperties;

    /**
     * 登录
     * @param openid 用户openid
     * @return 返回投诉用户表实体
     */
    public WxMember login(@NotNull(message = "请指定用户openId") String openid) {
        var member = wxmemberDao.selectByOpenId(openid);
        if (member == null) return null;
        member.setToken(buildUserToken(member.getUserid()));
        return member;
    }

    /**
     * 注销
     */
    public BoolResult logout() {
        var member = xajtfbService.getCurrentWxMember();
        wxmemberDao.deleteById(member.getUserid());
        return BoolResult.success();
    }

    /**
     * 注册
     * @param entity 用户
     */
    @OperateLog(msg = "新建微信用户", module = "投诉用户表")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult<WxMember> register(WxMember entity) {
        if (wxmemberDao.existxByOpenId(entity.getWechatid())) {
            wxmemberDao.update(entity);
        } else {
            var cacheKey = PHONE_CAPTCHA_CACHE_KEY + entity.getPhone() + entity.getSerialNumber();
            var captcha = phoneCaptchaCache.get(cacheKey, String.class);
            if (StrUtil.isBlank(captcha) || !entity.getPhoneCaptcha().equals(captcha)) {
                return BoolResult.fail("手机验证码无效");
            }
            phoneCaptchaCache.evict(cacheKey);

            //新建时如果主键未指定,自动设置主键
            entity.setUserid(XCI.guid());
            entity.setRegistereddate(new Date());
            wxmemberDao.insert(entity);
        }
        var model = login(entity.getWechatid());
        return BoolResult.success(model);
    }

    /**
     * 生成用户Token
     * @param userId 用户主键
     */
    public String buildUserToken(String userId) {
        GMap map = GMap.newMap(Const.USER_ID, XCI.encrypt(userId));
        Date expireDate = DateUtil.date().offset(DateField.DAY_OF_MONTH, 60);
        return XCI.createJwt(map, Const.JWT_SECRET_KEY, expireDate);
    }

    /**
     * 修改投诉用户表
     * @param entity 投诉用户表实体
     */
    @OperateLog(msg = "修改投诉用户表", module = "投诉用户表")
    @Transactional(rollbackFor = Exception.class)
    public BoolResult update(WxMember entity) {
        wxmemberDao.update(entity);
        return BoolResult.success();
    }

    /**
     * 创建验证码
     */
    public CaptchaDataModel buildPhoneCaptcha(BuildPhoneCaptchaBody body) {
        if (XCI.isBlank(body.getPhone()) || XCI.isBlank(body.getSerialNumber()) || body.getSerialNumber().length() != 37) {
            throw new ValidException("请按国家规范请求");
        }
        var width = body.getWidth();
        var height = body.getHeight();
        var type = body.getType();
        if (width == null || height <= 0) {
            width = 160;
        }
        if (height == null || height <= 0) {
            height = 48;
        }
        if (XCI.isBlank(type)) {
            type = "math";
        }
        var result = coreService.buildCaptcha(width, height, type);
        phoneCaptchaCache.put(body.getPhone() + body.getSerialNumber(), result.getValue());
        return result;
    }

    public BoolResult sendPhoneCaptcha(SendPhoneCaptchaBody body) {
        var unionKey = body.getPhone() + body.getSerialNumber();
        String captcha = phoneCaptchaCache.get(unionKey, String.class);
        if (body.getPhoneCaptcha() == null || captcha == null || (XCI.isBlank(body.getPhoneCaptcha()))
                || (XCI.isBlank(captcha)) || (!captcha.equalsIgnoreCase(body.getPhoneCaptcha()))) {
            return BoolResult.fail("验证码无效");
        }

        var times = XCI.getCacheValue(phoneDayTimesLimitCache, body.getPhone(), () -> 0);
        if (times > smsProperties.getDayTimesLimit()) {
            return BoolResult.fail("您的手机号码已达到发送验证码次数上限");
        }

        phoneCaptchaCache.evict(unionKey);
        String phoneCaptcha = RandomUtil.randomNumbers(4);
        sendSmsCode(body.getPhone(), phoneCaptcha);
        phoneCaptchaCache.put(PHONE_CAPTCHA_CACHE_KEY + unionKey, phoneCaptcha);
        phoneDayTimesLimitCache.put(body.getPhone(), times + 1);
        return BoolResult.success();
    }

    /**
     * 发送短信验证码
     * @param code 验证码
     */
    public void sendSmsCode(String phone, String code) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        var json = StrUtil.format("{\"OpenId\":\"{}\",\"PhoneNumbers\":\"{}\",\"Message\": \"{}\"}", smsProperties.getOpenid(), phone, StrUtil.format(smsProperties.getTpl(), code));
        HttpEntity<String> request = new HttpEntity<>(json, headers);
        var result = restTemplate.postForEntity(smsProperties.getUrl(), request, String.class).getBody();
        log.info("sms {} result:{}", json, result);
        log.info("sms {}", json);
    }

    /**
     * 根据主键查询单个投诉用户表
     * @param userid 投诉用户表主键
     * @return 返回投诉用户表实体
     */
    public WxMember selectById(@NotNull(message = "请指定投诉用户表主键") String userid) {
        return wxmemberDao.selectById(userid);
    }

    /**
     * 查询投诉用户表列表
     * @param filter 过滤条件
     * @return 返回投诉用户表列表
     */
    public List<WxMember> selectList(WxMemberFilter filter) {
        return wxmemberDao.selectList(filter);
    }

    /**
     * 查询投诉用户表分页列表
     * @param filter 过滤条件
     * @return 返回投诉用户表分页列表
     */
    public PageList<WxMember> selectPageList(WxMemberFilter filter) {
        return PageList.of(wxmemberDao.selectPageList(filter));
    }

}